package main

import (
	"app-box/config"
	"app-box/database"
	"app-box/handler"
	"app-box/handler/my_validator"
	"app-box/models/tables"
	"app-box/timingtask"
	"app-box/tools/caller"
	"flag"
	"fmt"
	"math/rand"
	"path"
	"time"

	"log"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/unrolled/secure"
)

var ConfigFilePath string

var randomLocalApiAddress string
var randomLocalStaticAddres string

func init() {
	flag.StringVar(&config.Mode, "mode", "Release", "set application run mode, availed values are Debug/Release/Test.")
	flag.StringVar(&ConfigFilePath, "config", "./config.ini", "specify the config file path")
	apiPort := 8000 + rand.Int()%2000
	staticPort := apiPort + 1
	randomLocalApiAddress = fmt.Sprintf("localhost:%d", apiPort)
	randomLocalStaticAddres = fmt.Sprintf("localhost:%d", staticPort)
}

func main() {
	initBasePath()
	// 解析参数
	flag.Parse()
	// 读取配置文件
	config.Init(ConfigFilePath)
	// 初始化数据库
	database.InitDB()
	// 初始化数据表
	tables.Init()
	// 启动定时服务
	timingtask.Start()
	if config.Server.StaticResourcePath != "" {
		// 启动静态文件服务
		go StartStaticFilesServer(SetupGin(false), randomLocalStaticAddres, true)
		// 启动api服务
		go StartAPIServer(SetupGin(false), true)
		// 启动反向代理服务，整合api服务和静态文件服务到一个端口
		// "/api" 开头的请求将转发给api服务，其他请求将转发给静态文件服务
		StartProxyServer(SetupGin(config.Server.HasTLS()))
	} else {
		// 只启动api服务
		StartAPIServer(SetupGin(config.Server.HasTLS()), false)
	}

}

func SetupGin(SSLRedirect bool) *gin.Engine {
	switch config.Mode {
	case config.DebugMode:
		gin.SetMode(gin.DebugMode)
	case config.TestMode:
		gin.SetMode(gin.TestMode)
	case config.ReleaseMode:
		gin.SetMode(gin.ReleaseMode)
	}
	engin := gin.New()
	engin.MaxMultipartMemory = config.System.MaxUploadFileSize
	my_validator.InitValidator()
	// 安全中间件
	secureMiddleware := secure.New(secure.Options{
		SSLRedirect:          SSLRedirect,
		AllowedHosts:         []string{".*"},
		AllowedHostsAreRegex: true,
	})

	secureHandler := func(c *gin.Context) {
		err := secureMiddleware.Process(c.Writer, c.Request)
		// If there was an error, do not continue.
		if err != nil {
			c.Abort()
			return
		}
		// Avoid header rewrite if response is a redirection.
		if status := c.Writer.Status(); status > 300 && status < 399 {
			c.Abort()
		}
	}
	engin.Use(secureHandler)
	return engin
}

// API服务
func StartAPIServer(engin *gin.Engine, isProxyMode bool) {
	if config.Mode == config.DebugMode || config.Mode == config.TestMode {
		engin.Use(gin.LoggerWithFormatter(func(params gin.LogFormatterParams) string {
			return fmt.Sprintf("[API] %s\t%s\n", params.Method, params.Path)
		}))
	}
	engin.Use(cors.New(cors.Config{
		AllowAllOrigins:  true,
		AllowMethods:     []string{"POST", "GET", "HEAD", "DELETE", "OPTION"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Sign", "Timestamp", "Authorization"},
		ExposeHeaders:    []string{},
		AllowCredentials: true,
		MaxAge:           12 * time.Hour,
	}))
	handler.SetupRoute(engin)
	if isProxyMode { // 如果是整合了静态文件服务的情况，是反向代理模式，不需要部分中间价
		log.Fatal(engin.Run(randomLocalApiAddress))
	} else {
		// 如果是仅API模式
		if config.Server.HasTLS() {
			log.Fatal(engin.RunTLS(config.Server.HttpsAddres(), config.Server.TLSCertificate, config.Server.TLSPrivateKey))
		} else {
			log.Fatal(engin.Run(config.Server.HttpAddres()))
		}
	}
}

// 静态文件服务
func StartStaticFilesServer(engin *gin.Engine, address string, isProxyMode bool) {
	static := handler.StaticResourceHandler{Path: config.Server.StaticResourcePath}
	static.InitRoute(engin)
	if config.Server.HasTLS() && !isProxyMode {
		log.Fatal(engin.RunTLS(address, config.Server.TLSCertificate, config.Server.TLSPrivateKey))
	} else {
		log.Fatal(engin.Run(address))
	}
}

// 启动反向代理
func StartProxyServer(engin *gin.Engine) {
	proxy := newProxyDirector()
	if config.Server.HasTLS() {
		// 添加中反向代理间件
		engin.Use(func(c *gin.Context) {
			proxy.ServeHTTP(c.Writer, c.Request)
		})
		go func() {
			log.Fatal(engin.Run(config.Server.HttpAddres()))
		}()
		log.Fatal(engin.RunTLS(config.Server.HttpsAddres(), config.Server.TLSCertificate, config.Server.TLSPrivateKey))
	} else {
		// 添加中反向代理间件
		engin.Use(func(c *gin.Context) {
			proxy.ServeHTTP(c.Writer, c.Request)
		})
		log.Fatal(engin.Run(config.Server.HttpAddres()))
	}

}

// 创建反向代理
func newProxyDirector() *httputil.ReverseProxy {
	director := func(req *http.Request) {
		if req.URL.Host != randomLocalApiAddress && req.URL.Host != randomLocalStaticAddres {
			req.URL.Scheme = "http"
			if strings.HasPrefix(req.RequestURI, "/api") {
				req.URL.Host = randomLocalApiAddress
			} else {
				req.URL.Host = randomLocalStaticAddres
			}
		}
	}
	return &httputil.ReverseProxy{Director: director}
}

func initBasePath() {
	callerInfo := caller.GetCaller(0)
	dir, _ := path.Split(callerInfo.FilePath)
	config.BasePath = dir
}
